package com.xiangxiao.rpan.utils;

import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;

import com.xiangxiao.rpan.utils.exception.DateException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

/**
 * 日期工具类
 * @auther xiangxiao
 * @email 573768011@qq.com
 * @data 2022/12/29 16:09
 */
public class DateUtil {
  public static final String FORMAT_DATE = "yyyyMMdd";
  public static final String FORMAT_DATE_CN = "yyyy-MM-dd";
  public static final String FORMAT_DATETIME = "yyyyMMddHHmmss";
  public static final String FORMAT_DATETIME_CN = "yyyy-MM-dd HH:mm:ss";
  public static final DateTimeFormatter DTF_DATE = DateTimeFormatter.ofPattern("yyyyMMdd");
  public static final DateTimeFormatter DTF_DATE_CN = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  public static final DateTimeFormatter DTF_DATETIME = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
  public static final DateTimeFormatter DTF_DATETIME_CN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  private static final String TIME_PATTERN = "HH:mm";
  private static final Calendar STATIC_CAL = new GregorianCalendar();
  private static final List<String> LIST_FORMAT = Arrays.asList("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyyMMddHHmmss", "yyyyMMdd");
  private static final List<DateTimeFormatter> LIST_DTF;
  /**
   * 标准的日期转换格式
   */
  private static final String STD_DAY_STR_FORMAT = "yyyy-MM-dd HH:mm:ss";

  /**
   * 标准时间转换类
   */
  private static final SimpleDateFormat STD_FORMAT = new SimpleDateFormat(STD_DAY_STR_FORMAT);

  /**
   * 日历实体
   */
  private static final Calendar CALENDAR_INSTANCE = Calendar.getInstance();;

  /**
   * 获取当前时间N天后的日期
   *
   * @param days
   * @return
   */
  public static Date afterDays(Integer days) {
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.DATE, days);
    return calendar.getTime();
  }

  /**
   * 获取特定时间N天后的日期
   *
   * @param date
   * @param days
   * @return
   */
  public static Date afterDays(Date date, Integer days) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(Calendar.DATE, days);
    return calendar.getTime();
  }

  /**
   * 获取当前时间N天前的日期
   *
   * @param days
   * @return
   */
  public static Date beforeDays(Integer days) {
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.DATE, -days);
    return calendar.getTime();
  }

  /**
   * 获取特定时间N天前的日期
   *
   * @param date
   * @param days
   * @return
   */
  public static Date beforeDays(Date date, Integer days) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(Calendar.DATE, -days);
    return calendar.getTime();
  }

  /**
   * 标准的日期转换成字符串
   *
   * @param date
   * @return
   */
  public static String stdDate2String(Date date) {
    return STD_FORMAT.format(date);
  }

  /**
   * 获取当前月份
   *
   * @param date
   * @return
   */
  public static int getYear(Date date) {
    return getAttribute(date, Calendar.YEAR);
  }

  /**
   * 获取当前月份
   *
   * @param date
   * @return
   */
  public static int getMonth(Date date) {
    return getAttribute(date, Calendar.MONTH) + 1;
  }

  /**
   * 获取当前是当月的第几天
   *
   * @param date
   * @return
   */
  public static int getDay(Date date) {
    return getAttribute(date, Calendar.DAY_OF_MONTH);
  }

  /**
   * 获取日历属性
   *
   * @param date
   * @param mode
   * @return
   */
  public static int getAttribute(Date date, int mode) {
    if (!Objects.isNull(date)) {
      CALENDAR_INSTANCE.setTime(date);
    } else {
      CALENDAR_INSTANCE.setTime(new Date());
    }
    return CALENDAR_INSTANCE.get(mode);
  }

  /**
   * 指定日期距离当前时间多少天
   *
   * @param date
   * @return
   */
  public static int diffDateCurrDay(Date date) {
    Calendar calendar1 = Calendar.getInstance();
    calendar1.setTime(date);
    Calendar today = Calendar.getInstance();
    int days = (int) ((today.getTimeInMillis() - calendar1.getTimeInMillis()) / (1000 * 60 * 60 * 24));
    return days;
  }

  public static String getDatePattern() {
    try {
      String defaultDatePattern = "yyyy-MM-dd HH:mm:ss";
      return defaultDatePattern;
    } catch (MissingResourceException var2) {
      throw var2;
    }
  }

  public static String getDateTimePattern() {
    return getDatePattern() + " HH:mm:ss";
  }

  public static String getDate(Date aDate) {
    String returnValue = "";
    if (aDate != null) {
      SimpleDateFormat df = new SimpleDateFormat(getDatePattern());
      returnValue = df.format(aDate);
    }

    return returnValue;
  }

  public static String getTimeNow(Date theTime) {
    return getDateTime("HH:mm", theTime);
  }

  public static String getDateTime(String aMask, Date aDate) {
    String returnValue = "";
    if (aDate != null) {
      SimpleDateFormat df = new SimpleDateFormat(aMask);
      returnValue = df.format(aDate);
    }

    return returnValue;
  }

  public static String convertDateToString(Date aDate) {
    if (aDate == null) {
      aDate = Calendar.getInstance(Locale.getDefault()).getTime();
    }

    return getDateTime(getDatePattern(), aDate);
  }

  public static String convertDateToString(Calendar calendar) {
    if (calendar == null) {
      calendar = Calendar.getInstance(Locale.getDefault());
    }

    return getDateTime(getDatePattern(), calendar.getTime());
  }

  public static String convertDateMSToString(Calendar calendar) {
    if (calendar == null) {
      calendar = Calendar.getInstance(Locale.getDefault());
    }

    String date = getDateTime(getDatePattern(), calendar.getTime());
    date = date + ".";
    date = date + calendar.get(14);
    return date;
  }

  public static String convertDateToNormalString(Date aDate) {
    return getDateTime("yyMMdd", aDate);
  }

  public static String convertDateToMonthString(Date aDate) {
    return getDateTime("yyMM", aDate);
  }

  public static String getLocalTime(String aMask) {
    if (aMask == null || aMask.isEmpty()) {
      aMask = getDatePattern();
    }

    return getDateTime(aMask, getLocalTime());
  }

  public static String getFormarMonth(Date date, String format) {
    SimpleDateFormat df = new SimpleDateFormat(format);
    return df.format(date);
  }

  public static String getLocaleDate() {
    Date aDate = Calendar.getInstance(Locale.getDefault()).getTime();
    return getDateTime(getDatePattern(), aDate);
  }

  public static Date convertStringToDate(String aMask, String strDate) throws ParseException {
    SimpleDateFormat df = new SimpleDateFormat(aMask);

    try {
      Date date = df.parse(strDate);
      return date;
    } catch (ParseException var5) {
      throw new ParseException(var5.getMessage(), var5.getErrorOffset());
    }
  }

  public static Calendar getToday() throws ParseException {
    Date today = new Date();
    SimpleDateFormat df = new SimpleDateFormat(getDatePattern());
    String todayAsString = df.format(today);
    Calendar cal = new GregorianCalendar();
    cal.setTime(convertStringToDate(todayAsString));
    return cal;
  }

  public static Date convertStringToDate(String strDate) throws ParseException {
    try {
      Date aDate = convertStringToDate(getDatePattern(), strDate);
      return aDate;
    } catch (ParseException var3) {
      var3.printStackTrace();
      throw new ParseException(var3.getMessage(), var3.getErrorOffset());
    }
  }

  public static Date getLocalTime() {
    Calendar cal = Calendar.getInstance(Locale.getDefault());
    return cal.getTime();
  }

  public static String getCurrentMonthFirstDateTime(String strDateFormat) {
    Calendar curCal = Calendar.getInstance();
    SimpleDateFormat datef = new SimpleDateFormat(strDateFormat);
    curCal.set(5, 1);
    Date beginTime = curCal.getTime();
    return datef.format(beginTime) + " 00:00:00";
  }

  public static String getCurrentMonthFirstDate(String strDateFormat) {
    Calendar curCal = Calendar.getInstance();
    SimpleDateFormat datef = new SimpleDateFormat(strDateFormat);
    curCal.set(5, 1);
    Date beginTime = curCal.getTime();
    return datef.format(beginTime);
  }

  public static boolean isTheSameMonth(Date d1, Date d2) {
    Calendar c1 = Calendar.getInstance();
    Calendar c2 = Calendar.getInstance();
    c1.setTime(d1);
    c2.setTime(d2);
    return c1.get(1) == c2.get(1) && c1.get(2) == c2.get(2);
  }

  public static boolean isTheSameDay(Date d1, Date d2) {
    Calendar c1 = Calendar.getInstance();
    Calendar c2 = Calendar.getInstance();
    c1.setTime(d1);
    c2.setTime(d2);
    return c1.get(1) == c2.get(1) && c1.get(2) == c2.get(2) && c1.get(5) == c2.get(5);
  }

  public static String getCurrentMonthLastDate() {
    Calendar cal = Calendar.getInstance();
    SimpleDateFormat datef = new SimpleDateFormat("yyyy-MM-dd");
    cal.set(5, 1);
    cal.roll(5, -1);
    Date endTime = cal.getTime();
    String endTimeStr = datef.format(endTime) + " 23:59:59";
    return endTimeStr;
  }

  public static String getFirstDayOfMonth(int year, int month) {
    Calendar cal = Calendar.getInstance();
    cal.set(1, year);
    cal.set(2, month - 1);
    cal.set(5, cal.getMinimum(5));
    return (new SimpleDateFormat("yyyy-MM-dd ")).format(cal.getTime()) + " 00:00:00";
  }

  public static String getLastDayOfMonth(int year, int month) {
    Calendar cal = Calendar.getInstance();
    cal.set(1, year);
    cal.set(2, month - 1);
    cal.set(5, cal.getActualMaximum(5));
    return (new SimpleDateFormat("yyyy-MM-dd ")).format(cal.getTime()) + " 23:59:59";
  }

  public static Date getDayOfBeforeMonth(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    cal.add(2, -1);
    return new Date(cal.getTimeInMillis());
  }

  public static Date getLocalTime(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    cal.add(11, -8);
    return cal.getTime();
  }

  public static int getYear() {
    return (new GregorianCalendar()).get(1);
  }

  public static int getMonth() {
    return (new GregorianCalendar()).get(2) + 1;
  }

  public static int getDay() {
    return (new GregorianCalendar()).get(5);
  }

  public static int getDateYear(Date date) {
    if (date == null) {
      return 0;
    } else {
      synchronized(STATIC_CAL) {
        return getStaticCalendars(date).get(1);
      }
    }
  }

  public static int getDateMonth(Date date) {
    if (date == null) {
      return 0;
    } else {
      synchronized(STATIC_CAL) {
        return getStaticCalendars(date).get(2) + 1;
      }
    }
  }

  public static int getDateDay(Date date) {
    if (date == null) {
      return 0;
    } else {
      synchronized(STATIC_CAL) {
        return getStaticCalendars(date).get(5);
      }
    }
  }

  public static int getDateHours(Date date) {
    if (date == null) {
      return 0;
    } else {
      synchronized(STATIC_CAL) {
        return getStaticCalendars(date).get(11);
      }
    }
  }

  public static int getDateMinutes(Date date) {
    if (date == null) {
      return 0;
    } else {
      synchronized(STATIC_CAL) {
        return getStaticCalendars(date).get(12);
      }
    }
  }

  public static int getDateSeconds(Date date) {
    if (date == null) {
      return 0;
    } else {
      synchronized(STATIC_CAL) {
        return getStaticCalendars(date).get(13);
      }
    }
  }

  public static int getWeekDay(Date date) {
    if (date == null) {
      return 0;
    } else {
      synchronized(STATIC_CAL) {
        return getStaticCalendars(date).get(7) - 1;
      }
    }
  }

  public static int indexOfYearWeek(Date date) {
    int year = getDateYear(date);
    return indexOfWeek(date, toDate(year, 1, 1));
  }

  public static int indexOfWeek(Date date, Date fromDate) {
    int days = diffDate(date, fromDate);
    int w0 = getWeekDay(fromDate);
    if (days >= 0) {
      if (w0 == 0) {
        return days / 7;
      } else {
        return days < 7 - w0 ? 0 : 1 + (days - (7 - w0)) / 7;
      }
    } else if (w0 == 0) {
      return -1 - (-days - 1) / 7;
    } else {
      return -days <= w0 ? 0 : -1 - (-days - 1 - w0) / 7;
    }
  }

  public static int diffDate(Date date1, Date date2) {
    return (int)((date1.getTime() - date2.getTime()) / 86400000L);
  }

  public static Date toDate(int year, int month, int day) {
    synchronized(STATIC_CAL) {
      STATIC_CAL.clear();
      STATIC_CAL.set(1, year);
      STATIC_CAL.set(2, month - 1);
      STATIC_CAL.set(5, day);
      STATIC_CAL.set(11, 0);
      STATIC_CAL.set(12, 0);
      STATIC_CAL.set(13, 0);
      return STATIC_CAL.getTime();
    }
  }

  public static long getDateTime(Object date) {
    return date instanceof Date ? ((Date)date).getTime() : 0L;
  }

  public static int getDaysOfMonth(int year, int month) {
    return (int)((toLongTime(month == 12 ? year + 1 : year, month == 12 ? 1 : month + 1, 1) - toLongTime(year, month, 1)) / 86400000L);
  }

  public static long toLongTime(int year, int month, int day) {
    return toLongTime(year, month, day, 0, 0, 0);
  }

  public static long toLongTime(int year, int month, int day, int hour, int min, int sec) {
    synchronized(STATIC_CAL) {
      STATIC_CAL.clear();
      STATIC_CAL.set(1, year);
      STATIC_CAL.set(2, month - 1);
      STATIC_CAL.set(5, day);
      STATIC_CAL.set(11, hour);
      STATIC_CAL.set(12, min);
      STATIC_CAL.set(13, sec);
      return STATIC_CAL.getTime().getTime();
    }
  }

  private static Calendar getStaticCalendars(Date date) {
    if (date != null) {
      STATIC_CAL.setTime(date);
    } else {
      STATIC_CAL.setTimeInMillis(System.currentTimeMillis());
    }

    return STATIC_CAL;
  }

  public static String dateToStr(Date date, String format) {
    if (null == date) {
      return null;
    } else {
      if (StringUtils.isBlank(format)) {
        format = "yyyy-MM-dd HH:mm:ss";
      }

      return date.toInstant().atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern(format));
    }
  }

  public static String dateToStr(LocalDateTime localDateTime) {
    return null == localDateTime ? null : DTF_DATETIME_CN.format(localDateTime);
  }

  public static String dateToStr(LocalDate localDate) {
    return null == localDate ? null : DTF_DATE_CN.format(localDate);
  }

  public static Date strToDate(String dateStr, String format) {
    Assert.notNull(StringUtils.isBlank(format) ? null : format, "DateUtil strToDate transfer exception format is null");
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
    Date date = null;

    try {
      date = simpleDateFormat.parse(dateStr);
      return date;
    } catch (ParseException var5) {
      throw new DateException("\u8f6c\u6362\u65f6\u95f4\u5f02\u5e38", var5);
    }
  }

  public static Date strToDate(String dateStr, DateTimeFormatter formatter) {
    Assert.notNull(formatter, "DateUtil strToDate transfer exception formatter is null");
    if (StringUtils.isBlank(dateStr)) {
      return null;
    } else {
      LocalDateTime localDateTime = LocalDateTime.parse(dateStr, formatter);
      return localToDate(localDateTime);
    }
  }

  public static Date strToDate(String dateStr) {
    if (StringUtils.isBlank(dateStr)) {
      throw new DateException("\u65f6\u95f4\u8f6c\u6362\u5f02\u5e38,\u53c2\u6570\u4e3a\u7a7a");
    } else {
      Iterator var1 = LIST_DTF.iterator();

      while(var1.hasNext()) {
        DateTimeFormatter dateTimeFormatter = (DateTimeFormatter)var1.next();
        LocalDateTime localDateTime = null;

        try {
          localDateTime = LocalDateTime.parse(dateStr, dateTimeFormatter);
        } catch (DateTimeParseException var7) {
          try {
            localDateTime = LocalDate.parse(dateStr, dateTimeFormatter).atStartOfDay();
          } catch (DateTimeParseException var6) {
          }
        }

        if (localDateTime != null) {
          Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
          if (date != null) {
            return date;
          }
        }
      }

      throw new DateException("\u65f6\u95f4\u8f6c\u6362\u5f02\u5e38\uff0c" + dateStr + "\u4e0d\u80fd\u8f6c\u6362\u4e3adate");
    }
  }

  public static Date localToDate(LocalDateTime localDateTime) {
    return null == localDateTime ? null : Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
  }

  public static <T> T dateToLocal(Date date, Class<T> clz) {
    if (null == date) {
      return null;
    } else if (LocalDateTime.class.equals(clz)) {
      return (T) date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    } else if (LocalDate.class.equals(clz)) {
      return (T) date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    } else if (LocalTime.class.equals(clz)) {
      return (T) date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
    } else {
      Assert.isTrue(false, "DateUtil dateToLocal transfer exception clz\u7c7b\u578b\u672a\u77e5");
      return null;
    }
  }

  public static Date addTime(Date date, long num, ChronoUnit chronoUnit) {
    LocalDateTime localDateTime = (LocalDateTime)dateToLocal(date, LocalDateTime.class);
    LocalDateTime plus = localDateTime.plus(num, chronoUnit);
    return localToDate(plus);
  }

  public static void main(String[] args) {
    Date date = strToDate("2021-01-01", "yyyy-MM-dd");
    System.out.println();
  }

  static {
    LIST_DTF = Arrays.asList(DTF_DATETIME_CN, DTF_DATE_CN, DTF_DATETIME, DTF_DATE);
  }
}
